package com.cloud.api.config;

import com.cloud.core.exception.MyException;
import jakarta.annotation.Resource;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;

/**
 * @author 杨斌
 * 2024-07-22/7/2024
 */
@Aspect
@Configuration
@Order(2)
public class RedisIdempotentAspect {
    @Resource(name = "myRedis")
    private RedisTemplate<String, Object> redisTemplate;
    @Around("execution(public * *(..)) && @annotation(com.cloud.api.config.Idempotent)")
    public Object interceptor(ProceedingJoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        Idempotent Idempotent = method.getAnnotation(Idempotent.class);
        if (StringUtils.isEmpty(Idempotent.keyPrefix())) {
            throw new MyException("exception.repeat.commit.noPrefix");
        }
        //获取自定义KEY
        final String lockKey = RequestKeyGenerator.getLockKey(joinPoint);
        // 使用RedisCallback接口执行set命令，设置锁键；设置额外选项：过期时间和SET_IF_ABSENT选项
        final Boolean success = redisTemplate.execute(
                (RedisCallback<Boolean>) connection -> connection.set(lockKey.getBytes(), new byte[0],
                        Expiration.from(Idempotent.timeout(), Idempotent.timeUnit()),
                        RedisStringCommands.SetOption.SET_IF_ABSENT));
        if (!success) {
            throw new MyException("exception.resubmit");
        }
        try {
            return joinPoint.proceed();
        } catch (Throwable throwable) {
            throw new MyException( "exception.error");
        }
    }
}
